package com.wxy.service.serviceimpl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wxy.dao.*;
import com.wxy.domain.*;
import com.wxy.service.ApiService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.FileInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author 47868
 */
@Service("ApiService")
public class ApiServiceImpl implements ApiService {
    private static final String PREFIX_PATH ="D:\\代码\\LibSystem\\userImg\\";
    private final UserDao userDao;
    private final BookDao bookDao;
    private final RestHighLevelClient client;
    private final BorrowRecordDao borrowRecordDao;
    private final UserBookDao userBookDao;
    private final RabbitTemplate rabbitTemplate;
    private final UserImgDao userImgDao;
    private final StringRedisTemplate stringRedisTemplate;
    public static final String NULL="null";

    @Autowired
    public ApiServiceImpl(UserDao userDao,
                          BookDao bookDao,
                          RestHighLevelClient client,
                          BorrowRecordDao borrowRecordDao,
                          UserBookDao userBookDao,
                          RabbitTemplate rabbitTemplate,
                          UserImgDao userImgDao,
                          StringRedisTemplate stringRedisTemplate) {
        this.userDao = userDao;
        this.bookDao = bookDao;
        this.client = client;
        this.borrowRecordDao = borrowRecordDao;
        this.userBookDao = userBookDao;
        this.rabbitTemplate = rabbitTemplate;
        this.userImgDao = userImgDao;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 用户搜索书籍
     * @param search 用户传入的用于查询的字符串
     * @return List<Book> 返回Book的集合
     */
    @Override
    public R getBookList(String search) throws IOException {
        SearchRequest request = new SearchRequest("book");
        SearchResponse response;
        List<Book> bookList= new ArrayList<>();
        /*没有查询条件,初始化查询，查询所有书籍数据*/
        if (NULL.equals(search)) {
            /*添加查询条件*/
            request.source().query(QueryBuilders.matchAllQuery());
            request.source().size(100);
            request.source().sort("bid");
            /*发送请求查询并返回response*/
            response=client.search(request, RequestOptions.DEFAULT);
            /*解析response返回List集合*/
            SearchHits hits = response.getHits();
            SearchHit[] searchHits = hits.getHits();
            for (SearchHit hit : searchHits) {
                String source = hit.getSourceAsString();
                Book book = JSONUtil.toBean(source, Book.class);
                bookList.add(book);
            }
            return R.ok("响应成功",bookList);
        }
        request.source().query(QueryBuilders.matchQuery("bookName", search));
        response=client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String source = hit.getSourceAsString();
            Book book = JSONUtil.toBean(source, Book.class);
            bookList.add(book);
        }
        return R.ok("响应成功",bookList);
    }

    /**
     * 用户借书
     * @param bookId 传入的书ID
     * @param userId 传入的用户ID
     * @return 返回信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R borrowBook(int bookId, int userId) {
        try {
            //判断book的数量是否大于0
            Book book = bookDao.selectOne(new QueryWrapper<Book>().eq("bid",bookId));
            int booknums=book.getNums();
            String bookName=book.getBookName();
            Long cardId=userDao.findCardById(userId);
            int userbooknums=userBookDao.getNumByUid(cardId);
            LocalDateTime localDateTime=LocalDateTime.now();
            LocalDateTime localDateTime1=localDateTime.plusDays(5);
            DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String nowTime=localDateTime.format(formatter);
            String plusTime=localDateTime1.format(formatter);
            if(booknums==0){
                return R.error(404,"该图书没有了");
            }
            //判断用户是否可以借书
            if(userbooknums==0){
                return R.error(403,"借阅失败，您的剩余可借数量不足，请先还书后借阅...");
            }
            //成功，借书逻辑：
            //1.添加借阅信息
            BorrowRecord borrowRecord=new BorrowRecord(
                    UUID.randomUUID().toString(),
                    cardId,
                    bookId,
                    bookName,
                    "未还",
                    nowTime,
                    plusTime
            );
            borrowRecordDao.insert(borrowRecord);
            //2.通过rabbit队列通知扣减book和user库存
            Map<String,Object> map=new HashMap<>();
            map.put("bookId",bookId);
            map.put("userId",cardId);
            rabbitTemplate.convertAndSend("dirSystem.direct", "red",map);
            //3.通过elastic search更新库存
            int num=bookId-10000;
            UpdateRequest request = new UpdateRequest("book",Integer.toString(num));
            request.doc(
                    "nums",(booknums-1)
            );
            client.update(request,RequestOptions.DEFAULT);
            return R.ok("借阅成功");
        }
        catch (Exception e){
            throw new MyException("还书异常"+e.getMessage());
        }
    }

    /**
     * 用户还书
     * @param borrowId 订单id
     * @param userId 用户id
     * @return 状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R returnBook(String borrowId, int userId) {
        try {
            //获取borrowId里的cardid和bookid
            QueryWrapper<BorrowRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("id", borrowId);
            BorrowRecord borrowRecord = borrowRecordDao.selectOne(queryWrapper);
            Long cardID=borrowRecord.getUid();
            int bookId=borrowRecord.getBid();
            //判断是否与登录校验传入的id一样
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("card_id", cardID);
            int uid=userDao.selectOne(userQueryWrapper).getId();
            if(!(uid ==userId)){
                throw new MyException("登录信息错误，请重试或联系管理员...");
            }
            //更新userbooknum数据库
            LocalDateTime localDateTime=LocalDateTime.now();
            DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String returnTime=localDateTime.format(formatter);
            borrowRecordDao.updateStatus(returnTime,borrowId);
            //发送rabbitmq请求
            Map<String,Object> map=new HashMap<>();
            map.put("bookId",bookId);
            map.put("userId",cardID);
            rabbitTemplate.convertAndSend("dirSystem.direct", "blue",map);
            //通知es修改数据
            int num=bookId-10000;
            int bookNums=bookDao.getBookNum(bookId);
            UpdateRequest request=new UpdateRequest("book",Integer.toString(num));
            request.doc(
                    "nums",(bookNums+1)
            );
            client.update(request,RequestOptions.DEFAULT);
            return R.ok("还书成功");
        }
        catch (Exception e) {
            throw new MyException("还书异常"+e.getMessage());
        }
    }
    /**
     * 借阅记录
     * @param search 用户搜索的书名
     * @return List<Borrow_Info> 返回集合
     */
    @Override
    public R getBorrowRecord(int id,String search) {
        List<BorrowRecord> borrowRecordList;
        List<Borrow_Info> borrowInfos=new ArrayList<>();
        QueryWrapper<BorrowRecord> queryWrapper = new QueryWrapper<>();
        Long cardId=userDao.findCardById(id);
        queryWrapper.eq("uid", cardId);
        if (NULL.equals(search)) {
            borrowRecordList=borrowRecordDao.selectList(queryWrapper);
            for (BorrowRecord borrowRecord : borrowRecordList) {
                borrowInfos.add(BeanUtil.toBean(borrowRecord, Borrow_Info.class));
            }
            return R.ok("查询成功",borrowInfos);
        }

        queryWrapper.eq("bookName", search);
        borrowRecordList=borrowRecordDao.selectList(queryWrapper);
        for (BorrowRecord borrowRecord : borrowRecordList) {
     borrowInfos.add(BeanUtil.toBean(borrowRecord, Borrow_Info.class));
     }
     return R.ok("查询成功",borrowInfos);
     }

     /**
     * 用户信息查询
     * @param id 要查询用户的id
     * @return User_Info 返回查询出的用户信息
     */
    @Override
    public R getUserInfo(int id) throws IOException {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        User user=userDao.selectOne(queryWrapper);
        UserBookNum userBookNum= userBookDao.selectOne(new QueryWrapper<UserBookNum>().eq("uid", user.getCardId()));
        //获取头像路径
        String imgPath=userImgDao.selectOne(new QueryWrapper<UserImg>().eq("uid", user.getCardId())).getPath();
        System.out.println(imgPath);
        //头像处理
        FileInputStream inputStream=new FileInputStream(PREFIX_PATH+imgPath);
        byte[] avatarBytes=inputStream.readAllBytes();
        String avatar=Base64.getEncoder().encodeToString(avatarBytes);
        //封装返回数据
        User_Info userInfo =BeanUtil.toBean(user, User_Info.class);
        userInfo.setNums(userBookNum.getNums());
        userInfo.setAvatar(avatar);
        inputStream.close();
        return R.ok("用户信息",userInfo);
    }
    @Override
    public String outLogin(int id){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        String username=userDao.selectOne(queryWrapper).getUsername();
        stringRedisTemplate.delete("user:"+username);
        return "login";
    }
}
